package com.vhall.component.controller.pay;

import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.model.OrderQueryModel;
import com.vhall.component.controller.pay.facade.WxPayFacade;
import com.vhall.component.entity.pay.entity.PaymentOrderEntity;
import com.vhall.component.framework.common.annotation.VhallComponent;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.utils.RedisLocker;
import com.vhall.component.service.pay.PaymentOrderService;
import com.vhall.component.service.pay.PaymentService;
import com.vhall.component.service.pay.SettingService;
import com.vhall.component.service.pay.constant.Channel;
import com.vhall.component.service.pay.constant.OrderStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author xuyongzheng
 * @since 2020/10/22
 **/
@Slf4j
@VhallComponent
public class WxPayController implements WxPayFacade {
    @Autowired
    private PaymentOrderService paymentOrderService;

    @Autowired
    private PaymentService paymentService;

    @Autowired
    private SettingService settingService;

    @Autowired
    private ThreadPoolTaskExecutor threadPool;

    @Override
    public String action(HttpServletRequest httpServletRequest) {
        String result = HttpKit.readData(httpServletRequest);
        log.info("微信回调:[{}]", result);
        Map<String, String> params = WxPayKit.xmlToMap(result);
        if (!WxPayKit.codeIsOk(params.get("return_code"))) {
            return "fail";
        }
        String outTradeNo = params.get("out_trade_no");
        if (StringUtils.isBlank(outTradeNo)) {
            return "fail";
        }
        RedisLocker.lock(outTradeNo, "1");
        try {
            PaymentOrderEntity order = paymentOrderService.findByBizOrderId(outTradeNo);
            if (order == null) {
                log.warn("回调对应订单不存在, out_trade_no:[{}]", outTradeNo);
                return "fail";
            }
            if (order.getStatus().equals(OrderStatus.SUCCESS)) {
                log.warn("订单已处理过, out_trade_no:[{}]", outTradeNo);
                return successXml();
            }
            Map<String, String> setting = settingService.mapBy(order.getAppId(), Channel.WEIXIN, order.getServiceCode());
            if (null == setting || setting.size() == 0) {
                throw new BusinessException(BizErrorCode.COMP_PAY_PARAM_UNSET);
            }
            if (!WxPayKit.verifyNotify(params, setting.get("key"), SignType.MD5)) {
                log.error("签名校验失败, params:[{}],key={}", params, setting.get("key"));
                return "fail";
            }
            // 主动查单, 确保收款成功
            if (!queryOrder(setting, order)) {
                return "fail";
            }
            LocalDateTime timeEnd = timeConvert(params.get("time_end"));
            int effect = paymentOrderService.doPayed(outTradeNo, params.get("transaction_id"),
                    Channel.WEIXIN, order.getServiceCode(), timeEnd);
            if (effect > 0) {
                //开始回调业务系统
                threadPool.execute(() -> paymentService.payNotify(order.getId()));
                return successXml();
            }
        } catch (Exception ex) {
            log.error("微信回调处理异常", ex);
        } finally {
            RedisLocker.unlock(outTradeNo, "1");
        }
        return "fail";
    }

    private LocalDateTime timeConvert(String time) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        return LocalDateTime.parse(time, dtf);
    }

    private Boolean queryOrder(Map<String, String> setting, PaymentOrderEntity order) {
        Map<String, String> queryParam = OrderQueryModel.builder()
                .appid(setting.get("appid"))
                .mch_id(setting.get("mchid"))
                .transaction_id(order.getTradeNo())
                .out_trade_no(order.getBizOrderNo())
                .nonce_str(WxPayKit.generateStr())
                .build()
                .createSign(setting.get("key"), SignType.MD5);
        String result = WxPayApi.orderQuery(queryParam);
        Map<String, String> params = WxPayKit.xmlToMap(result);
        if (!WxPayKit.codeIsOk(params.get("return_code"))) {
            return false;
        }
        return "SUCCESS".equals(params.get("result_code"));
    }

    private String successXml() {
        Map<String, String> xml = new HashMap<>(8);
        xml.put("return_code", "SUCCESS");
        xml.put("return_msg", "OK");
        return WxPayKit.toXml(xml);
    }

}
